#define _CRT_SECURE_NO_WARNINGS 1

// 动态内存管理
//#include <stdio.h>

// 我们发现，空间开辟大小是固定的
// 数组在申明的时候，必须指定数组的长度，数组空间一旦确定了大小不能调整

// 对于空间的需求，却不仅仅是上述的情况，有时候我们需要的空间大小在程序运行的时候才知道
// 数组编译时候开辟空间的方式就不能满足了

//int main()
//{
//	int n = 0; //
//	char ch = 'a';
//
//	// 数组 - 存放一个班级的数学成绩
//	int arr[10] = { 0 };
//
//
//	return 0;
//}

// malloc
// void* malloc(size_t size);
// 函数向内存申请一块连续可用的空间，并返回指向这块空间的指针
// 如果开辟成功，则返回一个指向开辟好空间的指针
// 如果开辟失败，则返回一个NULL指针，因此malloc的返回值一定要做检查
// 返回值的类型是void*，所以malloc函数并不知道开辟空间的类型，具体在使用的时候由使用者来决定
// 如果size为0，行为是未定义的，由编译器决定

//#include <stdio.h>
//#include <stdlib.h>
//
//int main()
//{
//	// int arr[10];
//	int* p = (int*)malloc(40);
//  int* p = (int*)calloc(10,sizeof(int));
//
//	if (p == NULL) {
//		perror("malloc");
//		return 1;
//	}
//
//	// 使用 - 给数组赋值
//	for (int i = 0; i < 10; i++) {
//		*(p + i) = i;
//		printf("%d ", *(p + i));
//	}
//	
//	// 释放空间
//	//free(p);
//  //p = NULL;
//
//	return 0;
//}

// void free(void* ptr);
// free函数用来释放动态开辟的内存
// 如果参数ptr指向的空间不是动态开辟的，则free函数的行为是未定义的
// 如果参数ptr是NULL指针，则函数什么事都不做

// calloc和realloc
// void* calloc (size_t num, size_t size);

// 函数的功能是为num个大小为size的元素开辟一块空间，并且把空间的每个字节初始化为0
// 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化全为0
// 所以如果要对申请的内存空间的内容要求初始化，则用calloc

// void* realloc (void* ptr, size_t size);
// realloc可以做到对动态开辟内存大小的调整

// ptr是要调整的内存地址
// size调整之后新大小
// 返回值为调整之后的内存起始位置
// 这个函数调整原内存空间大小的基础上，还会将原来内存中的数据移动到新的空间

//#include <stdio.h>
//#include <stdlib.h>
//
//int main()
//{
//	int* p = (int*)calloc(10, sizeof(int));
//
//	if (p == NULL)
//	{
//		perror("malloc");
//		return 1;
//	}
//
//	for (int i = 0; i < 10; i++) {
//		printf("%d ", *(p + i));
//	}
//
//	// 空间不够，想要扩大空间
//	p = realloc(p, 20 * sizeof(int));
//
//	// realloc调整空间失败，会返回NULL
//	// 即使是调整空间成功，也会有2种情况
//	// 
//	// 情况1：在已经开辟好的空间后面，没有足够的空间，直接进行空间的扩大
//	// 在这种情况下，realloc函数会在内存的堆区重新找一个空间（满足新的空间的大小需求）
//	// 同时会把旧的数据拷贝到新的新空间，然后释放旧的空间，同时返回新的空间的起始地址
//
//	// 情况2：在已经开辟好的空间后边，有足够的空间，直接进行扩大
//	// 扩大空间后，直接返回旧的空间的起始地址
//
//	return 0;
//}

// realloc函数的第一个参数为NULL，则相当于malloc

// 常见动态内存的错误

// 1.对NULL指针的解引用操作
// 2.对动态开辟空间的越界访问

//#include <stdio.h>
//#include <stdlib.h>
//
//int main()
//{
//	int* p = (int*)malloc(40);
//
//	if (p == NULL) return 1;
//
//	// 使用
//	for (int i = 0; i < 10; i++) {
//		*(p + i) = i; // 当循环到11次时候就越界访问了
//	}
//
//	free(p);
//	p = NULL;
//
//	return 0;
//}

// 3.对非动态开辟内存使用free释放

//#include <stdio.h>
//#include <stdlib.h>
//
//int main()
//{
//	int a = 10;
//	int* p = (int*)malloc(40);
//
//	if (p == NULL)
//	{
//		return 1;
//	}
//
//	// 使用
//	p = &a; // p指向的空间就不再是堆区上的空间,并且找不到原来的空间了
//	free(p);
//	p = NULL;
//
//	return 0;
//}

// malloc/calloc/realloc如果不主动释放，除了作用域是不会销毁的
// 释放的方式：
// 1.直到程序结束，才由操作系统回收
// 2.free主动释放

//#include <stdio.h>
//#include <stdlib.h>
//
//int* test()
//{
//	int* p = (int*)malloc(40);
//
//	if (p == NULL) return NULL;
//	else return p;
//}
//
//int main()
//{
//	// 出了test()函数作用域，不会被销毁
//	int ret = test();
//
//	return 0;
//}

// 使用free函数释放动态内存的一部分 error
// 对同一块内存空间的多次释放

//#include <stdio.h>
//#include <stdlib.h>
//
//void GerMemory(char* p) // 形参
//{
//	p = (char*)malloc(100);
//}
//
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(str); // 实参
//	strcpy(str, "Hello World!");
//	printf(str);
//
//}
//
//int main()
//{
//	Test();
//
//	return 0;
//}

//#include <stdio.h>
//#include <stdlib.h>
//
//char* GetMemory(void)
//{
//	char* p = "Hello, world!";
//	return p;
//}
//
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory(); // 这时候就是野指针了，已经没有使用权限了
//	printf(str);
//}
//
//int main()
//{
//	Test();
//
//	return 0;
//}
// 返回栈空间的地址是一个非常不好的行为

// 柔性数组
// C99种，结构种的最后一个元素允许是未知大小的数组，被称作柔性数组

// 1.在结构体中
// 2.最后一个成员
// 3.未知大小的数组
// 这个数组就是柔性数组

//struct st_type
//{
//	int i;
//	int a[0]; // 柔性数组成员
//};

// 无法编译的话，0删掉
// 柔性数组的特点：
// 结构中的柔性数组成员前面必须至少一个其他成员
// sizeof返回的这种结构大小不包括柔性数组的内存
// 包含柔性数组成员的结构用malloc()函数进行内存的动态分配
// 并且分配的内存应该大于结构的大小，以适应柔性数组的预期大小

//struct St
//{
//	char c;
//	int n;
//	int arr[0];//
//};
//
//int main()
//{
//	//struct St s = {0};
//	//printf("%d\n", sizeof(struct St));
//	struct St* ps = (struct St*)malloc(sizeof(struct St) + 10 * sizeof(int));
//	if (ps == NULL)
//	{
//		perror("malloc");
//		return 1;
//	}
//	ps->c = 'w';
//	ps->n = 100;
// 
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		ps->arr[i] = i;
//	}
//	//数组空间不够
//	struct St* ptr = realloc(ps, sizeof(struct St) + 15 * sizeof(int));
//	if (ptr != NULL)
//	{
//		ps = ptr;
//	}
//	else
//	{
//		perror("realloc");
//		return 1;
//	}
//	//...继续使用
//
//	for (i = 10; i < 15; i++)
//	{
//		ps->arr[i] = i;
//	}
//
//	for (i = 0; i < 15; i++)
//	{
//		printf("%d ", ps->arr[i]);
//	}
//	printf("\n%d\n", ps->n);
//	printf("%c\n", ps->c);
//
//	//释放
//	free(ps);
//	ps = NULL;
//
//	return 0;
//}

//#include <stdio.h>
//#include <stdlib.h>
//
//struct St
//{
//	char c;
//	int n;
//	int* arr;
//};
//
//int main()
//{
//	//struct St s = {0};
//	//printf("%d\n", sizeof(struct St));
//	struct St* ps = (struct St*)malloc(sizeof(struct St));
//	if (ps == NULL)
//	{
//		perror("malloc");
//		return 1;
//	}
//	ps->c = 'w';
//	ps->n = 100;
//
//	ps->arr = (int*)malloc(10 * sizeof(int));
//	if (ps->arr == NULL)
//	{
//		perror("malloc-2");
//		return 1;
//	}
//	//使用
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		ps->arr[i] = i;
//	}
//
//	//数组空间不够
//	int* ptr = (int*)realloc(ps->arr, 15 * sizeof(int));
//	if (ptr == NULL)
//	{
//		perror("realloc");
//		return 1;
//	}
//	else
//	{
//		ps->arr = ptr;
//	}
//	//使用
//	for (i = 10; i < 15; i++)
//	{
//		ps->arr[i] = i;
//	}
//	for (i = 0; i < 15; i++)
//	{
//		printf("%d ", ps->arr[i]);
//	}
//	printf("\n%d\n", ps->n);
//	printf("%c\n", ps->c);
//
//	//释放
//	free(ps->arr);
//	ps->arr = NULL;
//
//	free(ps);
//	ps = NULL;
//
//	return 0;
//}
// 柔性数组好处在于：
// 1.方便内存释放
// 2.这样有利于访问速度，减少内存碎片，提高空间利用率

// 局部性原理：
// 1.空间局限性
// 2.时间局限性

